package cn.qiao.netty.test;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.util.ByteProcessor;
import io.netty.util.internal.AppendableCharSequence;
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.SipMessage;
import io.pkts.packet.sip.impl.SipParser;

import java.io.IOException;
import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkPositive;

/**
 * sip对象解析
 *
 * @author carzy
 * @date 2020/8/10
 */
public class SipDecoder extends ByteToMessageDecoder {

    private static final SipResponseStatus UNKNOWN_STATUS = new SipResponseStatus(999, "Unknown");

    /**
     * 默认一行的最大长度
     */
    public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH = 4096;
    /**
     * 默认头信息的最大行数
     */
    public static final int DEFAULT_MAX_HEADER_SIZE = 8192;
    public static final int DEFAULT_MAX_CHUNK_SIZE = 8192;
    public static final boolean DEFAULT_VALIDATE_HEADERS = true;
    public static final int DEFAULT_INITIAL_BUFFER_SIZE = 128;

    private static final String EMPTY_VALUE = "";

    private final int maxChunkSize;
    protected final boolean validateHeaders;

    private volatile boolean resetRequested;


    /**
     * These will be updated by splitHeader方法.
     */
    private CharSequence name;
    /**
     * These will be updated by splitHeader方法.
     */
    private CharSequence value;

    /**
     * 报文解析
     */
    private final MessageParser parser;


    private long chunkSize;
    private long contentLength = Long.MIN_VALUE;


    /**
     * 构造器
     * {@code maxInitialLineLength (4096}}, {@code maxHeaderSize (8192)}, and
     * {@code maxChunkSize (8192)}.
     */
    public SipDecoder() {
        this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE);
    }

    /**
     * Creates a new instance with the specified parameters.
     */
    public SipDecoder(
            int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
        this(maxInitialLineLength, maxHeaderSize, maxChunkSize, DEFAULT_VALIDATE_HEADERS);
    }

    /**
     * Creates a new instance with the specified parameters.
     */
    public SipDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
        this(maxInitialLineLength, maxHeaderSize, maxChunkSize, validateHeaders, DEFAULT_INITIAL_BUFFER_SIZE);
    }

    /**
     * Creates a new instance with the specified parameters.
     */
    public SipDecoder(
            int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
            boolean validateHeaders, int initialBufferSize) {
        checkPositive(maxInitialLineLength, "maxInitialLineLength");
        checkPositive(maxHeaderSize, "maxHeaderSize");
        checkPositive(maxChunkSize, "maxChunkSize");

        AppendableCharSequence seq = new AppendableCharSequence(initialBufferSize);
        parser = new MessageParser(seq, maxHeaderSize);
        this.maxChunkSize = maxChunkSize;
        this.validateHeaders = validateHeaders;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf buffer, List<Object> out) {
        try {
            byte[] data = new byte[buffer.readableBytes()];
            buffer.readBytes(data);
            SipMessage frame = SipParser.frame(Buffers.wrap(data));
            out.add(frame);
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 消息头解析 报文
     */
    private static class MessageParser implements ByteProcessor {
        private final AppendableCharSequence seq;
        private final int maxLength;
        private int size;

        MessageParser(AppendableCharSequence seq, int maxLength) {
            this.seq = seq;
            this.maxLength = maxLength;
        }

        public SipMessage parse(ByteBuf buffer) {
            SipMessage sipMessage;
            buffer.forEachByte(this);
            try {
                sipMessage = SipParser.frame(Buffers.wrap(seq.toString()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            reset();
            return sipMessage;
        }

        public void reset() {
            size = 0;
        }

        @Override
        public boolean process(byte value) throws Exception {
            char nextByte = (char) (value & 0xFF);
            if (nextByte == 0) {
                int len = seq.length();
                // Drop CR if we had a CRLF pair
                if (seq.charAtUnsafe(len - 1) == HttpConstants.LF) {
                    return false;
                }
            }
            seq.append(nextByte);
            return true;
        }

    }
}
